package j.nawks.wx.api.process;

import j.nawks.wx.api.message.convert.TypeConvertProcess;
import j.nawks.wx.api.message.entity.request.ReqMessage;
import j.nawks.wx.api.process.encode.EncodeProcess;
import j.nawks.wx.api.process.interceptor.LastDefaultInterceptor;
import j.nawks.wx.api.process.interceptor.TextInterceptor;
import j.nawks.wx.api.utils.ClassUtils;
import j.nawks.wx.api.utils.MessageFormatUtils;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;

public class ProcessBus {

	private final static String REQUEST_MESSAGE_ENTITY_PACKAGE = "j.nawks.wx.api.message.entity.request";
	private List<Interceptor<? extends ReqMessage>> interceptorChain;

	private static ProcessBus bus = new ProcessBus();

	private ProcessBus() {
		interceptorChain = new LinkedList<Interceptor<? extends ReqMessage>>();
		interceptorChain.add(new TextInterceptor());
		interceptorChain.add(new LastDefaultInterceptor());
	}

	public static ProcessBus getInstance() {
		return bus;
	}

	public String process(String requestXML) {
		ReqMessage message = this._paramXMLToMessage(requestXML);
		// message.setRequestText(requestXML);

		// message.setResponseText(responseXML);
		System.out.println(message);
		String reponseXML = this._process(message);
		System.out.println("request>>>"+requestXML);
		System.out.println("response>>>"+reponseXML);
		return reponseXML;
	}

	public String _process(ReqMessage message) {
		ProcessContext cxt = new ProcessContext();
		for (Interceptor interrceptor : interceptorChain) {
			interrceptor.doInterrcept(message, cxt);
		}
		return EncodeProcess.getInstance().process(cxt.getRepMessage());
	}

	private ReqMessage _paramXMLToMessage(String requestXML) {
		ReqMessage message = null;
		Map<String, String> valueMap = new HashMap<String, String>();

		InputStream ins = null;

		try {
			/**
			 * 微信的内容由XML组成,并且最多只有一级,并没有父子节点
			 */
			ins = new ByteArrayInputStream(requestXML.getBytes("utf-8"));

			XMLInputFactory factory = XMLInputFactory.newInstance();
			XMLEventReader reader = factory.createXMLEventReader(ins);
			while (reader.hasNext()) {
				XMLEvent event = reader.nextEvent();
				if (event.isStartElement()) {
					String tagName = event.asStartElement().getName()
							.toString();
					if (!tagName.equals("xml")) {
						String text = reader.getElementText();
						valueMap.put(tagName, text);
					}
				}
			}

			message = this._buildMessage(valueMap);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (FactoryConfigurationError e) {
			e.printStackTrace();
		} catch (XMLStreamException e) {
			e.printStackTrace();
		}

		return message;
	}

	/**
	 * 将扁平化的内部数据转成有结构的消息实体类
	 * 
	 * @param valueMap
	 * @return
	 */
	private ReqMessage _buildMessage(Map<String, String> valueMap) {
		/**
		 * 获取消息类型,并根据实现预定义规则构造实体类对象
		 */
		String msgType = valueMap.get(MessageFormatUtils.MSGTYPE);
		ReqMessage msg = null;
		String className = null;
		if (MessageFormatUtils.EVENT_MESSAGE.equals(msgType)) {
			String eventType = valueMap.get(MessageFormatUtils.EVENT_TYPE);
			className = String.format("%s.%s%sEventMessage",
					REQUEST_MESSAGE_ENTITY_PACKAGE, eventType.substring(0, 1)
							.toUpperCase(), eventType.substring(1));
		} else {
			className = String.format("%s.%s%sMessage",
					REQUEST_MESSAGE_ENTITY_PACKAGE, msgType.substring(0, 1)
							.toUpperCase(), msgType.substring(1));
		}

		msg = (ReqMessage) ClassUtils.instance(className);
		this._fillProperties(valueMap, msg);
		return msg;
	}

	private void _fillProperties(Map<String, String> valueMap,
			ReqMessage message) {
		List<Field> fields = ClassUtils.getFields(message.getClass(), true);
		for (Field field : fields) {
			String fieldName = field.getName();
			String propName = fieldName.substring(0, 1).toUpperCase()
					+ fieldName.substring(1);
			Object value = TypeConvertProcess.getInstance().getValue(
					field.getGenericType(), valueMap.get(propName));
			if (value != null) {
				try {
					field.set(message, value);
				} catch (IllegalArgumentException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}
			}
		}
	}
}
